/** @file GOTHuart.c This file contains the source code for the UART API of the GOTH
 * Kick-ass Etherboard.
 * The UART is configured for:
 *	- 9600 baud
 *	- no parity
 *	- 8 data bits
 *	- 1 stop bit
 *	- no flow control
 * Sending is done by polling, receiving data is done on an interrupt driven basis. When characters
 * are received, they are stored in a 32-byte circular software buffer. Use UART_ReadBuffer to read
 * this data. When more than 32 bytes are received, the first bytes will be overwritten.
 *
 * @author Geoffrey Ottoy - Tom Hamelinckx
 * @date 01/07/2011 (last modified)
 *
 * <pre>
 * MODIFICATION HISTORY:
 *
 * Who  Date     Changes
 * ---- -------- ---------------------------------------------------------
 * GO   30/06/11 Created this file.
 * GO   01/07/11 Solved the UART error on reception of more than
 *               RECV_BUFFER_SIZE bytes. (By overwriting previously
 *               received bytes.
 * </pre>
 * 
 */

#include "GOTHuart.h"

/// The instance of the circular software receive buffer
/*volatile ?*/ C_BUFFER recv_buffer;

/** Initialize the UART for operation.
 *
 * @return	nothing
 */
void UART_InitUart(void){	
	U2BRG = BAUDRATEREG;	// set baudrate
	U2MODE = 0x300;
	///@todo check if CTS and RTS pins don't interfere with other functionality
	U2STA = 0;
	U2MODEbits.UARTEN = 1;	// enable uart
	U2STAbits.UTXEN = 1;	// enable transmitter
	
	// Assign UART2RX to pin RP6
	RPINR19bits.U2RXR = 6;
	AD1PCFGLbits.PCFG6 = 1; // since this pin is also CN6 (analog) we need to make sure it
							// is configured as digital pin
	// Assign UART2TX To Pin RP7
	RPOR3bits.RP7R = 5;

	U2MODEbits.BRGH = 0;	// Bit3 16 clocks per bit period
	U2MODEbits.PDSEL = 0;	// Bits1,2 8bit, No Parity
	U2MODEbits.STSEL = 0;	// Bit0 One Stop Bit

	// UART nSHDN (necessary for Kick-Ass Etherboard)
	TRISAbits.TRISA9 = 0;
	
	// reset interrupt flags
 	IFS1bits.U2RXIF = 0;
    IFS1bits.U2TXIF = 0;
	IFS4bits.U2ERIF = 0;
	
	// Interrupts on character receive/transmit
	U2STAbits.UTXISEL1 = 0;
	U2STAbits.UTXISEL0 = 0;
	U2STAbits.URXISEL1 = 0;
	U2STAbits.URXISEL0 = 0;
	// Enable interrupts
	//IEC1bits.U2TXIE = 1; // we don't use the TX interrupt
	IEC1bits.U2RXIE = 1;
	IEC4bits.U2ERIE = 1;
	
	// Interrupt priority
	///@todo set interrupt priority
	
	// Init buffer
	recv_buffer.in_pointer = 0;
	recv_buffer.out_pointer = 0;
	recv_buffer.fill_count = 0;
	
	// Enable the RS-232 driver of the Kick-ass Etherbord
	UART_TransceiverOn();
}

/** Turn on the UART hardware RS-232 driver. Accordingly the green indication LED off the
 * bord will be lit.
 *
 * @return	nothing
 */
void UART_TransceiverOn(void){
	PORTAbits.RA9 = 1; // connected to the MAX3222E nSHDN pin (and to the indication led)
}

/** Turn off the UART hardware RS-232 driver. Accordingly the green indication LED off the
 * bord will be off.
 *
 * @return	nothing
 */
void UART_TransceiverOff(void){
	PORTAbits.RA9 = 0; // connected to the MAX3222E nSHDN pin (and to the indication led)
}

/** Read data from the UART software circular buffer.
 * 
 * @param	buffer	A pointer to the buffer where the bytes will be stored
 * @param	length	Determines how many bytes will be read
 *
 * @return	The number of bytes read. This will be smaller than length, when less bytes were
 *			available
 */
int UART_ReadBuffer(unsigned char * buffer, int length){
	int i = 0;

	while((i < length) && (recv_buffer.fill_count != 0)){
		buffer[i] = recv_buffer.buffer[recv_buffer.out_pointer];
		recv_buffer.fill_count--;
		recv_buffer.out_pointer = (recv_buffer.out_pointer + 1) & RECV_BUFFER_MASK;
		i++;
	}

	return i;
}

/** Sent data over the UART
 * 
 * @param	buffer	A pointer to the buffer that needs to be sent
 * @param	length	Determines how many bytes will be sent
 *
 * @return	The number of bytes sent.
 */
int UART_WriteBuffer(unsigned char * buffer, int length){
	int i;
	for(i =0; i < length; i++){ // loop until all bytes are sent
		while(U2STAbits.UTXBF == 1); // wait while transmission is ongoing
		U2TXREG = buffer[i];
	}
	return i;
}

/** The UART2 TX ISR. Triggered when a character is received.
 *
 * @return	nothing
 */
void __attribute__((interrupt, no_auto_psv)) _U2RXInterrupt(void){
	
	// Check for overrun error (GO: not sure when this happens)
	if(U2STAbits.OERR == 1){ // overrun error
		U2STAbits.OERR = 0; // clear error
		// take appropriate action
	}

	if(recv_buffer.fill_count == RECV_BUFFER_SIZE){
		// keep filling the buffer, but overwrite previous data
		recv_buffer.buffer[recv_buffer.in_pointer] = U2RXREG;
		recv_buffer.in_pointer = (recv_buffer.in_pointer + 1) & RECV_BUFFER_MASK;
		// since we overwrite the byte, we need to reposition our out_pointer too
		recv_buffer.out_pointer = (recv_buffer.out_pointer + 1) & RECV_BUFFER_MASK;
		/** @warning reading the data from the U2RXREG is necessary. This is because next time,
		 * when data is to the pic, a UART error will be thrown.
		 */
	}
	else{
		recv_buffer.buffer[recv_buffer.in_pointer] = U2RXREG;
		recv_buffer.in_pointer = (recv_buffer.in_pointer + 1) & RECV_BUFFER_MASK;
		recv_buffer.fill_count++;
	}
	
	IFS1bits.U2RXIF = 0;      //clear UART2 RX interrupt flag
}

/** The UART2 TX ISR. This is not used since the TX interrupt is disabled.
 *
 * @return	nothing
 */
void __attribute__((interrupt, no_auto_psv)) _U2TXInterrupt(void){
	///@todo add code e.g. send_counter++;
	
	IFS1bits.U2TXIF = 0;      //clear UART2 TX interrupt flag
}

/** The UART2 error ISR.
 *
 * @return	nothing
 */
void __attribute__((interrupt, no_auto_psv)) _U2ErrInterrupt(void){
	///@todo add error code	

	IFS4bits.U2ERIF = 0;      //clear UART2 Error interrupt flag
}
